#include <linux/types.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <net/genetlink.h>

enum {
	DEMO_CMD_UNSPEC = 0,	/* Reserved */
	DEMO_CMD_ECHO,			/* user->kernel request/get-response */
	DEMO_CMD_REPLY,			/* kernel->user event */
	__DEMO_CMD_MAX,
};
#define DEMO_CMD_MAX (__DEMO_CMD_MAX - 1)

enum {
	DEMO_CMD_ATTR_UNSPEC = 0,
	DEMO_CMD_ATTR_MESG,		/* demo message  */
	DEMO_CMD_ATTR_DATA,		/* demo data */
	__DEMO_CMD_ATTR_MAX,
};
#define DEMO_CMD_ATTR_MAX (__DEMO_CMD_ATTR_MAX - 1)


#define DEMO_GENL_FAMILY_NAME "DEMO"

static const struct nla_policy demo_cmd_policy[DEMO_CMD_ATTR_MAX+1] = {
	[DEMO_CMD_ATTR_MESG]	= { .type = NLA_STRING },
	[DEMO_CMD_ATTR_DATA]	= { .type = NLA_S32 },	
};

static int demo_echo_cmd(struct sk_buff *skb, struct genl_info *info);

static struct genl_ops demo_ops[] = {
    {
        .cmd = DEMO_CMD_ECHO,
        .doit = demo_echo_cmd,
    },
};

static struct genl_family demo_family = {
    .module = THIS_MODULE,
    .ops = demo_ops,
    .n_ops = ARRAY_SIZE(demo_ops),
    .name = DEMO_GENL_FAMILY_NAME,
    .version = 1,
    .maxattr = DEMO_CMD_ATTR_MAX,
    .policy = demo_cmd_policy,
};

static inline int genl_msg_prepare_usr_msg(u8 cmd, size_t size, pid_t pid, struct sk_buff **skbp)
{
    struct sk_buff *skb;
 
    /* create a new netlink msg */
    skb = genlmsg_new(size, GFP_KERNEL);
    if (skb == NULL) {
        return -ENOMEM;
    }
 
    /* Add a new netlink message to an skb */
    genlmsg_put(skb, pid, 0, &demo_family, 0, cmd);
 
    *skbp = skb;
    return 0;
}
 
static inline int genl_msg_mk_usr_msg(struct sk_buff *skb, int type, void *data, int len)
{
    int rc;
 
    /* add a netlink attribute to a socket buffer */
    if ((rc = nla_put(skb, type, len, data)) != 0) {
        return rc;
    }
    return 0;
}
 
/**
* genl_msg_send_to_user - 通过generic netlink发送数据到netlink
*
* @data: 发送数据缓存
* @len:  数据长度 单位：byte
* @pid:  发送到的客户端pid
*
* return:
*    0:       成功
*    -1:      失败
*/
int genl_msg_send_to_user(void *data, int len, pid_t pid)
{
    struct sk_buff *skb;
    size_t size;
    void *head;
    int rc;
 
    size = nla_total_size(len); /* total length of attribute including padding */
 
    rc = genl_msg_prepare_usr_msg(DEMO_CMD_ECHO, size, pid, &skb);
    if (rc) {
        return rc;
    }
 
    rc = genl_msg_mk_usr_msg(skb, DEMO_CMD_ATTR_MESG, data, len);
    if (rc) {
        kfree_skb(skb);
        return rc;
    }
 
    head = genlmsg_data(nlmsg_data(nlmsg_hdr(skb)));
 
    genlmsg_end(skb, head);
 
    rc = genlmsg_unicast(&init_net, skb, pid);
    if (rc < 0) {
        return rc;
    }
 
    return 0;
}

#define TEST_GENL_MSG_FROM_KERNEL   "Hello from kernel space!!!"
static int demo_echo_cmd(struct sk_buff *skb, struct genl_info *info)
{
    /* message handling code goes here; return 0 on success, negative values on failure */
    struct nlmsghdr *nlhdr;
    struct genlmsghdr *genlhdr;
    struct nlattr *nlh;
    char *str;
    int ret;

    printk("hello: demo_echo_cmd start \n");
    nlhdr = nlmsg_hdr(skb);
    genlhdr = nlmsg_data(nlhdr);
    nlh = genlmsg_data(genlhdr);
    str = nla_data(nlh);
    printk("doc_exmpl_echo get: %s\n", str);
 
    ret = genl_msg_send_to_user(TEST_GENL_MSG_FROM_KERNEL, strlen(TEST_GENL_MSG_FROM_KERNEL) + 1,  nlhdr->nlmsg_pid);
 
    return ret;
}

static int __init netlink_test_init(void)
{
    int ret;

    printk("hello: hello netlink");

    ret = genl_register_family(&demo_family);

    if (ret < 0) {
        printk("hello module register generic netlink failed!\n");
    } else {
        printk("hello module register generic netlink success, demo_family.id is [%d] \n", demo_family.id);
    }

    return 0;
}

static void __exit netlink_test_exit(void)
{
    genl_unregister_family(&demo_family);
    printk("hello module netlink exit! \n");
}

module_init(netlink_test_init);
module_exit(netlink_test_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("netlink test");